﻿using System;
using System.Collections.Generic;
using System.Data;
using System.Reflection;
using System.Collections;

namespace OF.Common
{
    /// <summary>
    /// 将DataTable对象转换成List列表.
    /// 通过数据库结果集中的列名映射数据实体的属性名.
    /// 转换条件:实体属性名称和数据库结果集中的列名保持一致,不区分大小写.
    /// 如果有不一致的映射,应加入字典mapper中,key为数据库结果集中的列名的大写形式,value为数据实体的属性名称
    /// </summary>
    public class DataConvertor
    {
        /// <summary>
        /// 将一个模型对象转换成另一个模型对象
        /// 转换条件:实体属性名称和数据库结果集中的列名保持一致,不区分大小写.
        /// 如果有不一致的映射,应加入字典mapper中,key为源类型属性名的大写形式,value为目标类型的属性名称
        /// </summary>
        /// <typeparam name="T">目标对象类型</typeparam>
        /// <typeparam name="TS">源对象类型</typeparam>
        /// <param name="model">源对象</param>
        /// <param name="mapper">(可选参数)</param>
        /// <returns></returns>
        public static T ToModel<T, TS>(TS model, Dictionary<string, string> mapper = null)
            where T : class, new()
        {
            if (mapper == null)
                mapper = new Dictionary<string, string>();

            var local = Activator.CreateInstance<T>();
            SetEntityUti(local, model, mapper);
            return local;
        }

        /// <summary>
        /// 将DataTable对象转换成List列表.
        /// 通过数据库结果集中的列名映射数据实体的属性名.
        /// 转换条件:实体属性名称和数据库结果集中的列名保持一致,不区分大小写.
        /// 如果有不一致的映射,应加入字典mapper中,key为数据库结果集中的列名的大写形式,value为数据实体的属性名称
        /// </summary>
        /// <typeparam name="T">List泛型集合实体类型</typeparam>
        /// <param name="dataTable">要转换的数据表</param>
        /// <param name="mapper">(可选参数)</param>
        /// <returns></returns>
        public static List<T> ToList<T>(DataTable dataTable, Dictionary<string, string> mapper = null)
        {
            if (mapper == null)
                mapper = new Dictionary<string, string>();
            var lst = new List<T>();
            foreach (DataRow dr in dataTable.Rows)
            {
                var local = Activator.CreateInstance<T>();
                SetEntityUti(local, dr, mapper);
                lst.Add(local);
            }
            return lst;
        }

        /// <summary>
        /// 将DataTable对象转换成List列表.
        /// 通过数据库结果集中的列名映射数据实体的属性名.
        /// 转换条件:实体属性名称和数据库结果集中的列名保持一致,不区分大小写.
        /// 如果有不一致的映射,应加入字典mapper中,key为数据库结果集中的列名的大写形式,value为数据实体的属性名称
        /// </summary>
        /// <typeparam name="T">List泛型集合实体类型</typeparam>
        /// <param name="dataSet">要转换的数据集</param>
        /// <param name="mapper">(可选参数)</param>
        /// <returns></returns>
        public static List<T> ToList<T>(DataSet dataSet, Dictionary<string, string> mapper = null)
        {
            if (dataSet == null || dataSet.Tables.Count == 0)
                return new List<T>();
            return ToList<T>(dataSet.Tables[0], mapper);
        }

        /// <summary>
        /// 将集合类转换成DataTable
        /// </summary>
        /// <param name="list">集合</param>
        /// <returns></returns>
        public static DataTable ToDataTable(IList list)
        {
            var result = new DataTable();
            if (list.Count > 0)
            {
                PropertyInfo[] propertys = list[0].GetType().GetProperties();
                foreach (PropertyInfo pi in propertys)
                {
                    result.Columns.Add(pi.Name, pi.PropertyType);
                }

                foreach (object t in list)
                {
                    var tempList = new ArrayList();
                    foreach (PropertyInfo pi in propertys)
                    {
                        object obj = pi.GetValue(t, null);
                        tempList.Add(obj);
                    }
                    object[] array = tempList.ToArray();
                    result.LoadDataRow(array, true);
                }
            }
            return result;
        }

        private const BindingFlags PUBLIC_INSTANCE_PROPERTY_BINDING_FLAGS = BindingFlags.CreateInstance | BindingFlags.Instance //实例
                                                                        | BindingFlags.Public //公有
                                                                        | BindingFlags.SetProperty //可写属性
                                                                        | BindingFlags.IgnoreCase; //忽略大小写

        #region 对象转换辅助方法

        /// <summary>
        /// 从源对象获取数据到目标对象
        /// </summary>
        /// <typeparam name="TS">源对象类型</typeparam>
        /// <param name="entity">实体，不允许为空</param>
        /// <param name="source">源对象，不允许为空</param>
        /// <param name="mapper">属性与字段名称的映射，Key：为字段名，必须都为大写；Value为属性名</param>
        private static void SetEntityUti<TS>(object entity, TS source, Dictionary<string, string> mapper)
        {
            SetEntityUti(entity, source, (pInfo, colName) =>
            {
                if (mapper.ContainsKey(colName.ToUpper()))
                {
                    SetEntityUti(entity, mapper[colName.ToUpper()], colName, source);
                    return false;
                }
                return true;
            });
        }

        /// <summary>
        /// 从源对象获取数据到目标对象
        /// </summary>
        /// <typeparam name="TS">源对象类型</typeparam>
        /// <param name="entity">实体，不允许为空</param>
        /// <param name="source">源对象，不允许为空</param>
        /// <param name="action">特殊处理，返回true时，就函数默认处理；返回false，函数就不处理</param>
        private static void SetEntityUti<TS>(object entity, TS source, Func<PropertyInfo, string, bool> action)
        {
            Type entityType = entity.GetType();
            Type tb = typeof(TS);
            PropertyInfo[] props = tb.GetProperties(PUBLIC_INSTANCE_PROPERTY_BINDING_FLAGS);
            foreach (PropertyInfo p in props)
            {
                object value = p.GetValue(source, null);
                string name = p.Name;

                PropertyInfo pi = entityType.GetProperty(name, PUBLIC_INSTANCE_PROPERTY_BINDING_FLAGS);
                if (action(pi, name) && pi != null)
                {
                    pi.SetValue(entity, value, null);
                }
            }
        }

        /// <summary>
        /// 从源对象获取数据到目标对象
        /// </summary>
        /// <typeparam name="TS">源对象类型</typeparam>
        /// <param name="entity">实体，不允许为空</param>
        /// <param name="propName">属性名，不允许为空</param>
        /// <param name="colName">源属性名，不允许为空</param>
        /// <param name="source">源对象，不允许为空</param>
        /// <remarks>一般不建议使用，效率问题</remarks>
        private static void SetEntityUti<TS>(object entity, string propName, string colName, TS source)
        {
            if (string.IsNullOrEmpty(propName)) throw new NullReferenceException("pInfo不允许为空");
            if (entity == null) throw new NullReferenceException("entity不允许为空");

            PropertyInfo pInfo = entity.GetType().GetProperty(propName, PUBLIC_INSTANCE_PROPERTY_BINDING_FLAGS);
            if (pInfo == null)
            {
                throw new Exception(propName + "属性在" + entity.GetType().FullName + "不存在");
            }

            PropertyInfo p = typeof(TS).GetProperty(colName, PUBLIC_INSTANCE_PROPERTY_BINDING_FLAGS);
            pInfo.SetValue(entity, p.GetValue(source, null), null);
        }

        #endregion 对象转换辅助方法

        #region 反射辅助方法

        /// <summary>
        /// 根据字段名称，寻找属性名称的默认规则
        /// </summary>
        /// <param name="colName">字段名称</param>
        /// <returns>返回实体属性名称</returns>
        private static string GetPropertyNameByColumnName(string colName)
        {
            return colName.Replace("_", "");
        }

        /// <summary>
        /// 从DataReader获取数据到实体
        /// </summary>
        /// <param name="entity">实体，不允许为空</param>
        /// <param name="source">DataReader对象，不允许为空</param>
        /// <param name="mapper">属性与字段名称的映射，Key：为字段名，必须都为大写；Value为属性名</param>
        private static void SetEntityUti(object entity, DataRow source, Dictionary<string, string> mapper)
        {
            SetEntityUti(entity, source, (pInfo, colName) =>
            {
                if (mapper.ContainsKey(colName.ToUpper()))
                {
                    SetEntityUti(entity, mapper[colName.ToUpper()], colName, source);
                    return false;
                }
                return true;
            });
        }

        /// <summary>
        /// 从DataReader获取数据到实体
        /// </summary>
        /// <param name="entity">实体，不允许为空</param>
        /// <param name="dr"></param>
        /// <param name="action">特殊处理，返回true时，就函数默认处理；返回false，函数就不处理</param>
        private static void SetEntityUti(object entity, DataRow dr, Func<PropertyInfo, string, bool> action)
        {
            Type entityType = entity.GetType();
            //循环dr的所有字段

            foreach (DataColumn col in dr.Table.Columns)
            {
                string colName = col.ColumnName;
                string propertyName = colName;
                PropertyInfo pInfo = entityType.GetProperty(propertyName, PUBLIC_INSTANCE_PROPERTY_BINDING_FLAGS);

                if (action(pInfo, colName) && pInfo != null)
                {
                    SetEntityUti(entity, pInfo, colName, dr);
                }
            }
        }

        /// <summary>
        /// 从DataReader获取数据到实体
        /// </summary>
        /// <param name="entity">实体，不允许为空</param>
        /// <param name="propName">属性名，不允许为空</param>
        /// <param name="colName">字段名，不允许为空</param>
        /// <param name="dr"></param>
        /// <remarks>一般不建议使用，效率问题</remarks>
        private static void SetEntityUti(object entity, string propName, string colName, DataRow dr)
        {
            if (string.IsNullOrEmpty(propName)) throw new NullReferenceException("pInfo不允许为空");
            if (entity == null) throw new NullReferenceException("entity不允许为空");

            PropertyInfo pInfo = entity.GetType().GetProperty(propName, PUBLIC_INSTANCE_PROPERTY_BINDING_FLAGS);
            if (pInfo == null)
            {
                throw new Exception(propName + "属性在" + entity.GetType().FullName + "不存在");
            }
            SetEntityUti(entity, pInfo, colName, dr);
        }

        /// <summary>
        /// 从DataReader获取数据到实体
        /// </summary>
        /// <param name="entity">实体，不允许为空</param>
        /// <param name="pInfo">属性对象，不允许为空</param>
        /// <param name="colName">字段名，不允许为空</param>
        /// <param name="dr"></param>
        /// <remarks>一般不建议使用，效率问题</remarks>
        private static void SetEntityUti(object entity, PropertyInfo pInfo, string colName, DataRow dr)
        {
            if (pInfo == null) throw new NullReferenceException("pInfo不允许为空");
            if (entity == null) throw new NullReferenceException("entity不允许为空");

            Type pType = pInfo.PropertyType;

            if (pType == typeof(int))
            {
                pInfo.SetValue(entity, GetDataByDataRow(dr, colName, DefaultData.INT), null);
            }
            else if (pType == typeof(int?))
            {
                pInfo.SetValue(entity, GetDataByDataRow<int>(dr, colName), null);
            }
            else if (pType == typeof(decimal))
            {
                pInfo.SetValue(entity, GetDataByDataRow(dr, colName, DefaultData.DECIMAL), null);
            }
            else if (pType == typeof(decimal?))
            {
                pInfo.SetValue(entity, GetDataByDataRow<decimal>(dr, colName), null);
            }
            else if (pType == typeof(Double))
            {
                pInfo.SetValue(entity, GetDataByDataRow(dr, colName, DefaultData.DOUBLE), null);
            }
            else if (pType == typeof(Double?))
            {
                pInfo.SetValue(entity, GetDataByDataRow<Double>(dr, colName), null);
            }
            else if (pType == typeof(DateTime))
            {
                pInfo.SetValue(entity, GetDataByDataRow(dr, colName, DefaultData.DATETIME), null);
            }
            else if (pType == typeof(DateTime?))
            {
                pInfo.SetValue(entity, GetDataByDataRow<DateTime>(dr, colName), null);
            }
            else if (pType == typeof(long))
            {
                pInfo.SetValue(entity, GetDataByDataRow(dr, colName, DefaultData.LONG), null);
            }
            else if (pType == typeof(long?))
            {
                pInfo.SetValue(entity, GetDataByDataRow<long>(dr, colName), null);
            }
            else if (pType == typeof(Boolean))
            {
                pInfo.SetValue(entity, GetDataByDataRow(dr, colName, DefaultData.BOOLEAN), null);
            }
            else if (pType == typeof(Boolean?))
            {
                pInfo.SetValue(entity, GetDataByDataRow<Boolean>(dr, colName), null);
            }
            else
            {
                pInfo.SetValue(entity, GetTrimString(dr, colName), null);
            }
        }

        private static T GetDataByDataRow<T>(DataRow dr, int index, T defaultT) where T : IConvertible
        {
            object obj2 = dr[index];
            if (IsNullValue(obj2))
            {
                return defaultT;
            }
            if (typeof(T) == typeof(string))
            {
                obj2 = obj2.ToString();
            }
            return (T)Convert.ChangeType(obj2, typeof(T));
        }

        private static T? GetDataByDataRow<T>(DataRow dr, int index) where T : struct, IConvertible
        {
            T? nullable = null;
            object obj2 = dr[index];
            if (!IsNullValue(obj2))
            {
                nullable = GetDataByDataRow(dr, index, default(T));
            }
            return nullable;
        }

        private static T GetDataByDataRow<T>(DataRow dr, string columnName, T defaultT) where T : IConvertible
        {
            object obj2 = dr[columnName];
            if (IsNullValue(obj2))
            {
                return defaultT;
            }
            if (typeof(T) == typeof(string))
            {
                obj2 = obj2.ToString();
            }
            return (T)Convert.ChangeType(obj2, typeof(T));
        }

        private static T? GetDataByDataRow<T>(DataRow dr, string columnName) where T : struct, IConvertible
        {
            T? nullable = null;
            object obj2 = dr[columnName];
            if (!IsNullValue(obj2))
            {
                nullable = GetDataByDataRow(dr, columnName, default(T));
            }
            return nullable;
        }

        private static string GetString(DataRow dr, int index)
        {
            return GetDataByDataRow(dr, index, string.Empty);
        }

        private static string GetString(DataRow dr, string columnName)
        {
            return GetDataByDataRow(dr, columnName, string.Empty);
        }

        private static string GetTrimString(DataRow dr, int index)
        {
            return GetString(dr, index).Trim();
        }

        private static string GetTrimString(DataRow dr, string columnName)
        {
            return GetString(dr, columnName).Trim();
        }

        private static bool IsNullValue(object obj)
        {
            return ((obj == null) || (obj is DBNull));
        }

        #endregion 反射辅助方法
    }

    /// <summary>
    /// 默认值
    /// </summary>
    public static class DefaultData
    {
        /// <summary>
        /// 默认数字
        /// </summary>
        public static readonly int INT = -1;

        /// <summary>
        /// 默认数字0
        /// </summary>
        public static readonly int INT0 = 0;

        /// <summary>
        /// 默认时间
        /// </summary>
        public static readonly DateTime DATETIME = DateTime.MinValue;

        /// <summary>
        /// 默认的DECIMAL
        /// </summary>
        public static readonly decimal DECIMAL = 0;

        /// <summary>
        /// 默认的double
        /// </summary>
        public static readonly double DOUBLE = 0;

        /// <summary>
        /// 默认long
        /// </summary>
        public static readonly long LONG = 0;

        /// <summary>
        /// 默认boolean
        /// </summary>
        public static readonly Boolean BOOLEAN = false;
    }
}
